package com.ganeshkrishnan.globald.web.controller;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.ganeshkrishnan.globald.dao.DeploymentDao;
import com.ganeshkrishnan.globald.dao.PersonDao;
import com.ganeshkrishnan.globald.dao.PersonLocationDao;
import com.ganeshkrishnan.globald.model.Deployment;
import com.ganeshkrishnan.globald.model.MilResource;
import com.ganeshkrishnan.globald.model.Person;
import com.ganeshkrishnan.globald.model.PersonLocation;
import com.ganeshkrishnan.globald.web.utils.Constants;

@Controller
@RequestMapping(value = "/deploy")
public class DeploymentController {

	@Autowired
	private DeploymentDao		deploymentdao;
	@Autowired
	private PersonLocationDao	personlocationdao;

	public DeploymentDao getDeploymentdao() {
		return deploymentdao;
	}

	public void setDeploymentdao(DeploymentDao deploymentdao) {
		this.deploymentdao = deploymentdao;
	}

	public PersonLocationDao getPersonlocationdao() {
		return personlocationdao;
	}

	public void setPersonlocationdao(PersonLocationDao personlocationdao) {
		this.personlocationdao = personlocationdao;
	}

	@Autowired
	private PersonDao	personDao;

	public PersonDao getPersonDao() {
		return personDao;
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public DeploymentController() {
	}

	// initializing method
	@RequestMapping(method = RequestMethod.GET)
	public String getCreateForm(Model model, HttpSession session) {
		return "territory";
	}

	@RequestMapping(value = "/getmydeployment", method = RequestMethod.POST)
	public String getMyLocation(@RequestParam int locID, Model model, HttpSession session) {
		Map<String, Object> map = new HashMap<String, Object>();
		Map<Object, Object> freeRes = new HashMap<Object, Object>();
		Long personID = (Long) session.getAttribute("personID");
		if (null == personID) {
			return "country";
		}
		//FIXME extract this up the calling chain so it's called once per session start and 
		//stored in javascript array. Less secure but faster
		List<Object[]> totalDep = personlocationdao.findTotalDeployed(personID);
		for (Object[] obj : totalDep) {
			freeRes.put(obj[0], obj[1]);
		}
		List<Deployment> list = deploymentdao.findDeploymentsByLocation(locID);
		for (Deployment dep : list) {
			map.put(dep.getResourceType() + "", dep.getAvailResources());
		}
		model.addAttribute("engagement", map);
		model.addAttribute("usedResources", freeRes);
		model.addAttribute("dep", locID);
		//model.addAttribute("engtype", map);
		return "deployment";
	}

	@RequestMapping(value = "/getcombatant", method = RequestMethod.POST)
	public String getCombatant(@RequestParam long locID, Model model, HttpSession session) {
		Long personID = (Long) session.getAttribute("personID");
		if (null == personID) {
			return "loggedout";
		}
		PersonLocation location = personlocationdao.get(locID);
		Person p = location.getPerson();
		String fname = p.getFirstName();
		String country = p.getCountryCode().getcName();
		int level = p.getLevel();
		int karma = p.getKarma();
		model.addAttribute("fname", fname);
		model.addAttribute("nationality", country);
		model.addAttribute("level", level);
		model.addAttribute("karma", karma);
		model.addAttribute("locID", locID);
		model.addAttribute("pid", p.getId());
		//model.addAttribute("engtype", map);
		return "enemy";
	}

	@RequestMapping(value = "/getmyterritory", method = RequestMethod.GET)
	public String getMyTerritory(Model model, HttpSession session) {
		Long personID = (Long) session.getAttribute("personID");
		if (null == personID) {
			return "loggedout";
		}
		//WTF is this? 
		model.addAttribute("allowadd", "true");
		return "territory";
	}

	/*
	 This is the main method for war. The use case of this method is as follows
	 1)Get player's free resources
	 2)Get enemys resourced deployed at this particular satellites
	 3)for each kind of military resource find out the difference d
	 4) Multiply d*hitpoint of that resource= totalpoint t
	 5)If t is -ve the player has lost else player has won
	 6)If player has lost 
	  		a) if d is positive 
	  				i)for each resource then update it to d else set it 0 
	  				ii)for each enemy resource at deployment update it to 0 else set it ABS(d)
	  7)If player has won
	  		a)update the owner of location to current player
	  		b)goto 6a
	 
	 
	  * If player wins: location captured with no deployment
		 * opponent remaining army freed for opponnet? or kill them all?
		 * 
		 * if player looses: remaining army in same free position
		 * opponent location decreased by casualty
		 * 
		 * eg
		 *   Army 12 vs 15  (5hp)
		 * Tank 7 vs 5  ( 10 hp)
		 * Navy 19 vs 19 (12 hp)
		 * Sub 12 vs 15 (20 hp)
		 * 
		 * A -3
		 * T 2
		 * N 0
		 * S -3
		 * 
		 * (-3*5) + (2*10) + (0*15) + (-3*20) =-55 LOSS
	 */
	@RequestMapping(value = "/getattackterritory", method = RequestMethod.POST)
	public String getAttackTerritory(@RequestParam long locID, Model model, HttpSession session) {
		Long personID = (Long) session.getAttribute("personID");
		if (null == personID) {
			personID = Constants.PERSONID;
			return "loggedout";
		}
		Person player = personDao.get(personID);
		PersonLocation location = personlocationdao.get((long) locID);
		//FIXME basic checks here to make sure no one is messin around with web injections
		if (null == location) {

			return "invalidaction";
		}
		Person enemy = location.getPerson();
		if (enemy.getCountryCode().getID() == player.getCountryCode().getID()) {

			return "invalidaction";
		}

		//get the deployed army of the player
		List<Object[]> totalDep = personlocationdao.findTotalDeployed(personID);
		Map<Object, Object> occupiedMap = new HashMap<Object, Object>();

		for (Object[] obj : totalDep) {
			//System.out.println("object is " + (obj[0]).toString());
			int resType = (Integer) obj[0];
			System.out.println("Enemy Occupied: Unit type" + resType + "/" + obj[1]);
			occupiedMap.put(resType, obj[1]);
		}

		//get the total army of the player
		Set<MilResource> milResources = player.getMilresource();

		//subtract the total from the deployed to get free army
		Map<Integer, Long> freeMap = new HashMap<Integer, Long>();
		for (MilResource milResource : milResources) {
			int unitType = milResource.getUnitType();
			long totalUnit = milResource.getMilAvailable();
			Object occupiedres = occupiedMap.get(unitType);
			if (null != occupiedres) {
				long availRes = totalUnit - Integer.parseInt(occupiedres.toString());
				freeMap.put(unitType, availRes);
				System.out.println("Free army:" + unitType + "/" + availRes);
			} else {
				//no unit of this type has been deployed to any location
				freeMap.put(unitType, totalUnit);
				System.out.println("xFree army:" + unitType + "/" + totalUnit);
			}

		}

		// get the enemy army at the deployment
		List<Deployment> list = deploymentdao.findDeploymentsByLocation(locID);

		Map<Integer, Long> warMap = new HashMap<Integer, Long>();

		int totalPoint = 0;
		//		Iterate through the deployment and match up with players free resources

		Set<Integer> keys = freeMap.keySet();
		for (Integer integer : keys) {
			int unitType = integer.intValue();
			Long myStrength = freeMap.get(integer);
			long enemyStrength = 0;
			for (Deployment dep : list) {
				if (dep.getResourceType() == unitType) {
					enemyStrength = dep.getAvailResources();
					System.out.println("enemy strength for unit type " + unitType + " is " + enemyStrength);
					break;
				}
			}
			long variance = myStrength - enemyStrength;
			System.out.println("variance for unittype " + unitType + " is " + variance);
			warMap.put(unitType, variance);
			totalPoint += (unitType + 1) * 5 * variance;
			System.out.println("total point=" + totalPoint);
		}

		//Winner has been decided
		boolean win = totalPoint > 0;
		model.addAttribute("win", win);

		// update the losses for  player regardless of outcome
		for (MilResource milResource : milResources) {
			Long leftOver = warMap.get(milResource.getUnitType());
			if (null == leftOver)
				continue;
			if (leftOver < 0)
				leftOver = 0l;
			milResource.setMilAvailable(leftOver);
			milResources.add(milResource);
		}
		player.setMilresource(milResources);
		personDao.save(player);

		//get the enemy and update his military resource too. Casualty is for both sides.
		//its a fucking war not a sauna bath

		Set<MilResource> enmilResources = enemy.getMilresource();
		for (MilResource milResource : enmilResources) {
			//BUGFIX occupied res is for player NOT enemy
			Object occupiedres = occupiedMap.get(milResource.getUnitType());
			Long leftOver = warMap.get(milResource.getUnitType());
			long ileftOver = 0;
			if (null != leftOver)
				ileftOver = leftOver.longValue();
			if (ileftOver < 0)
				ileftOver = Math.abs(ileftOver);
			else
				ileftOver = 0;

			long totalUnit = milResource.getMilAvailable();
			long unitAvail = 0;
			if (null != occupiedres) {
				unitAvail = totalUnit - Long.parseLong(occupiedres.toString()) + ileftOver;
			}
			milResource.setMilAvailable(unitAvail);
			enmilResources.add(milResource);
		}
		enemy.setMilresource(enmilResources);
		personDao.save(enemy);

		//Now decide the spoils
		if (win) {
			//First update the location! Make the player as the owner
			location.setDeployment(null);
			location.setPerson(player);
		} else {
			// we have friggin lost the battle. Update the deployed resources for the enemy and then exit
			//subtract the remaining resources from the deployment 
			for (Deployment dep : list) {
				Long depMil = warMap.get(dep.getResourceType());

				if (null != depMil) {
					//int intdepMil = Integer.parseInt(depMil.toString());
					long leftOver = depMil.longValue();
					long milLost = leftOver <= 0 ? Math.abs(leftOver) : 0;
					dep.setAvailResources(milLost);
					deploymentdao.save(dep);
				}
			}

		}

		return "aterritory";
	}

	@Transactional
	@RequestMapping(value = "/addmydeployment", method = RequestMethod.GET)
	public @ResponseBody
	Map<String, Object> addMyLocation(@RequestParam int locID, @RequestParam int m0, @RequestParam int m1,
			@RequestParam int m2, @RequestParam int m3, @RequestParam int m4, @RequestParam int m5, Model model,
			HttpSession session) {
		//FIXME check if the deployment number < total resources available
		Map<String, Object> map = new HashMap<String, Object>();
		Long personID = (Long) session.getAttribute("personID");
		if (null == personID) {
			map.put(Constants.STATUS, "-ER");
			map.put(Constants.CODE, "401");
			return map;
		}
		PersonLocation location = personlocationdao.get((long) locID);
		if (location != null) {
			//WTF?
			//personlocationdao.remove((long) locID);
		} else {
			map.put(Constants.STATUS, "-ER");
			map.put(Constants.CODE, "403");
			return map;
		}
		//WTF saving the location should propagate and update it's children but hibernate seems
		//oblivious to the children. I can't seem to get my brain around this issue so will do a
		// brute update of its children here
		Set<Deployment> depSet = location.getDeployment();
		for (Deployment dep : depSet) {
			deploymentdao.remove(dep.getID());
		}
		Set<Deployment> tempset = new HashSet<Deployment>();
		Deployment dep0 = new Deployment();
		dep0.setResourceType(0);
		dep0.setAvailResources(m0);
		//dep0.setLocationID(locID);
		dep0.setPersonLocation(location);
		deploymentdao.save(dep0);
		tempset.add(dep0);
		Deployment dep1 = new Deployment();
		dep1.setResourceType(1);
		dep1.setAvailResources(m1);
		dep1.setPersonLocation(location);
		deploymentdao.save(dep1);
		tempset.add(dep1);
		Deployment dep2 = new Deployment();
		dep2.setResourceType(2);
		dep2.setAvailResources(m2);
		dep2.setPersonLocation(location);
		tempset.add(dep2);
		deploymentdao.save(dep2);
		Deployment dep3 = new Deployment();
		dep3.setResourceType(3);
		dep3.setAvailResources(m3);
		dep3.setPersonLocation(location);
		deploymentdao.save(dep3);
		tempset.add(dep3);
		Deployment dep4 = new Deployment();
		dep4.setResourceType(4);
		dep4.setAvailResources(m4);
		dep4.setPersonLocation(location);
		tempset.add(dep4);
		deploymentdao.save(dep4);
		Deployment dep5 = new Deployment();
		dep5.setResourceType(5);
		dep5.setAvailResources(m5);
		dep5.setPersonLocation(location);
		tempset.add(dep5);
		deploymentdao.save(dep5);
		//		Set<Deployment> depSet = location.getDeployment();
		//		for (Deployment dep : depSet) {
		//			int i = dep.getResourceType();
		//			dep.setAvailResources(300);
		//		}
		//		
		//		depSet.addAll(tempset);
		//		location.setDeployment(depSet);
		//		personlocationdao.save(location);
		//personlocationdao.update(location);
		return map;
	}
}
